import { Feature } from "ol";
import { MultiLineString, Point } from "ol/geom";
import { Vector as VectorLayer } from "ol/layer";
import { unByKey } from "ol/Observable";
import { fromLonLat, transform } from "ol/proj";
import { getVectorContext } from "ol/render";
import { Vector as VectorSource } from "ol/source";
import { Stroke, Style, Icon } from "ol/style";
import CurvesSource from "./curvesSource";

const defaultOptions = {
    pointPositions: [[[140.80, 15.90], [143.60, 33.00]], [[140.80, 15.90], [154.216463, 42.895035]], [[140.80, 15.90], [105.941956, 47.07053]], [[140.80, 15.90], [100.718274, 0.95006]]],
    splitLength: 180,
    oneFrameLimitTime: 0,
    radialColor: {
        0: '#BBFFFF',
        0.2: '#AEEEEE',
        0.4: '#96CDCD',
        0.6: '#668B8B',
        0.8: '#98F5FF',
        1: '#8EE5EE'
    },
    controlRatio: 1.0,
}

export default class CurvesLayer {


    constructor(options) {

        let __options = Object.assign(defaultOptions, options)
        // console.log(__options);
        this.map = __options.map
        // 保存 点的位置的数组 => [ 线 , 线 ] => [ [点,点] , [ 点 , 点] ]
        this.pointPositions = __options.pointPositions
        // 两个点... 开始点 跟结束点的features  一维的数组 [开始点 ,结束点 , 开始点,结束点] 
        this.pointsFeatures = []
        // 分割线段长度 越高 则曲线越准确
        this.splitLength = __options.splitLength
        // 一帧耗时多少毫秒
        this.oneFrameLimitTime = __options.oneFrameLimitTime
        // colorstop
        this.radialColor = __options.radialColor
        // 控制点 的 比例 或者说弯曲程度
        this.controlRatio = __options.controlRatio

        this.curProjection = this.map.getView().getProjection()
        this.isEPSG4326 = this.curProjection === 'EPSG:4326' ? true : false

        this.eventKey = null

        this.pointsFeatures = this.generatePointsFeatures(this.pointPositions)

        this.init()
    }


    init() {
        let layer = new VectorLayer({
            desc: 'curvesLayer'
        })
        let source = new VectorSource()

        let curDateTime = new Date().getTime()
        this.eventKey = layer.on('postrender', (evt) => {
            let frameRenderTime = new Date().getTime() - curDateTime
            let features = this.pointsFeatures
            for (let i = 0; i < features.length; i += 2) {
                let curFeatureCurvesSource = features[i].get('CurvesSource')
                let { times, lineCoords, startPos, endPos, controlPos, lastEndPos, arrowImage, arrowLoad } = curFeatureCurvesSource.getRenderState()
                if (times > this.splitLength) {
                    times = 0
                    lineCoords = []
                    lastEndPos = startPos
                }
                let curEndPos = this.bezierSquareCalc(startPos, controlPos, endPos, times / this.splitLength);
                if (this.isEPSG4326) {
                    lineCoords.push([lastEndPos, curEndPos])
                } else {
                    lineCoords.push([transform(lastEndPos, 'EPSG:4326', this.curProjection), transform(curEndPos, 'EPSG:4326', this.curProjection)])
                }
                let ctx = getVectorContext(evt)
                //  曲线的渲染
                // 为了找到 开始点 的屏幕坐标 以及 结束点的屏幕坐标..
                this.curveLineRender(ctx, lineCoords, startPos, endPos)
                // 箭头的渲染
                this.arrowRender(ctx, arrowImage, arrowLoad, lastEndPos, curEndPos)
                times++
                lastEndPos = curEndPos;
                if (frameRenderTime > this.oneFrameLimitTime) {
                    curFeatureCurvesSource.setRenderState({ times, lineCoords, startPos, endPos, lastEndPos, arrowImage, arrowLoad })
                    curDateTime += this.oneFrameLimitTime
                }
            }
            layer.changed()
        })
        source.addFeatures(this.pointsFeatures)
        layer.setSource(source)
        this.curvesLayer = layer
        this.curvesSource = source
        this.map.addLayer(layer)
    }

    /**
     * 根据 pointPositions 生成 point Features
     * @param {*} pointPositions 
     * @returns pointsFeatures 
     */
    generatePointsFeatures(pointPositions) {
        let pointsFeatures = []
        for (let i = 0, ii = pointPositions.length; i < ii; i++) {
            let registeFeature = new Feature({
                geometry: new Point(transform(pointPositions[i][0], 'EPSG:4326', this.curProjection)),
                CurvesSource: new CurvesSource({
                    startPos: pointPositions[i][0],
                    endPos: pointPositions[i][1],
                    controlRatio: this.controlRatio
                }),
            })
            registeFeature.setId((i + Math.random()).toString())
            pointsFeatures.push(registeFeature, new Feature({ geometry: new Point(transform(pointPositions[i][1], 'EPSG:4326', this.curProjection)) }))
        }
        return pointsFeatures
    }

    /**
     * 曲线的渲染
     * @param {*} ctx 绘制的上下文 
     * @param {*} lineCoords 推进的 multiLineString 数组
     * @param {*} startPos 开始点 经纬度表示
     * @param {*} endPos 结束点 经纬度表示
     */
    curveLineRender(ctx, lineCoords, startPos, endPos) {
        let geometry = new MultiLineString(lineCoords)
        let startGrdPixelPos = this.map.getPixelFromCoordinate(fromLonLat(startPos))
        let endGrdPixelPos = this.map.getPixelFromCoordinate(fromLonLat(endPos))
        let xDiff = endGrdPixelPos[0] - startGrdPixelPos[0]
        let yDiff = endGrdPixelPos[1] - startGrdPixelPos[1]
        let radius = Math.pow(Math.pow(xDiff, 2) + Math.pow(yDiff, 2), 0.5)
        // console.log(startGrdPixelPos,endGrdPixelPos);
        var grd = ctx.context_.createRadialGradient(startGrdPixelPos[0], startGrdPixelPos[1], 0, startGrdPixelPos[0], startGrdPixelPos[1], radius)
        let radialColor = this.radialColor
        for (let i in radialColor) {
            grd.addColorStop(i, radialColor[i])
        }
        ctx.setStyle(new Style({
            stroke: new Stroke({
                color: grd,
                width: 3,
            })
        }))
        ctx.drawGeometry(geometry)
    }

    /**
     * 箭头的渲染
     * @param {*} ctx 绘制的上下文 
     * @param {*} arrowImage 箭头的htmlElement
     * @param {*} arrowLoad 图片是否加载完成
     * @param {*} lastEndPos 上一个点的结束坐标 经纬度表示
     * @param {*} curEndPos 当前的结束点坐标 经纬度表示
     */
    arrowRender(ctx, arrowImage, arrowLoad, lastEndPos, curEndPos) {
        let arrowGeometry
        if (this.isEPSG4326) {
            arrowGeometry = new Point(curEndPos)
        } else {
            arrowGeometry = new Point(transform(curEndPos, 'EPSG:4326', this.curProjection))
        }
        // geometrys
        const dx = curEndPos[0] - lastEndPos[0];
        const dy = curEndPos[1] - lastEndPos[1];
        const rotation = Math.atan2(dy, dx);
        if (arrowLoad) {
            ctx.setImageStyle(new Icon({
                img: arrowImage,
                imgSize: [16, 16],
                rotateWithView: true,
                rotation: -rotation,
            }))
        }
        // 渲染
        ctx.drawGeometry(arrowGeometry)
    }

    // 线性插值 函数 ... 此处的计算 只处理  二维  带x ,y 的 向量
    linearInterpolation(startPos, endPos, t) {
        let a = this.constantMultiVector2(1 - t, startPos)
        let b = this.constantMultiVector2(t, endPos)
        return this.vector2Add(a, b)
    }

    // 常数乘以二维向量数组 的函数
    constantMultiVector2(constant, vector2) {
        return [constant * vector2[0], constant * vector2[1]];
    }

    vector2Add(a, b) {
        return [a[0] + b[0], a[1] + b[1]]
    }

    // 求贝塞尔曲线上的点  通过公式 求得点
    bezierSquareCalc(startPos, center, endPos, t) {
        let a = this.constantMultiVector2(Math.pow((1 - t), 2), startPos)
        let b = this.constantMultiVector2((2 * t * (1 - t)), center)
        let c = this.constantMultiVector2(Math.pow(t, 2), endPos) // => []
        return this.vector2Add(this.vector2Add(a, b), c)
    }

    /**
     * 根据点位置添加一条曲线
     * @param {*} addPositions 点位置 => [ [ 点 , 点]]
     */
    addCurves(addPositions) {
        let resFeatures = this.generatePointsFeatures(addPositions)
        this.pointPositions.push(...addPositions)
        this.pointsFeatures.push(...resFeatures)
        this.curvesSource.addFeaturesInternal(resFeatures)
    }

    // 刷新 lineCoords  用于状态一致 
    refreshCurvesCoords() {
        let features = this.pointsFeatures
        for (let i = 0; i < features.length; i += 2) {
            let curFeatureCurvesSource = features[i].get('CurvesSource')
            curFeatureCurvesSource.reset()
        }
    }

    /**
     * 这个方法会同步得删除在图层内的features.
     * @param {*} index 移除的索引
     */
    removeFeatureByIndex(index) {
        let features = this.curvesSource.getFeatures()
        let realFeatureIndex = index * 2
        this.curvesSource.removeFeature(features[realFeatureIndex])
        this.curvesSource.removeFeature(features[realFeatureIndex + 1])
    }

    // 提供此方法 用于删除所有相关的图层
    destroy() {
        // 其他的变量由js 自己控制gc 就可
        this.eventKey && unByKey(this.eventKey)
        this.pointPositions = []
        this.pointsFeatures = []
        this.map.removeLayer(this.curvesLayer)
    }

}